use memmap2::{MmapMut, MmapOptions};
use std::fs::File;
use std::io;

/// 物理内存映射管理器，使用 VolatileCell 包装数据以实现 volatile 访问
pub struct Regi {
    mmap: MmapMut,
}

impl Regi {
    /// 成功时返回 `Regi` 实例，失败时返回 `io::Error`
    pub fn new(phys_addr: u64, size: usize) -> io::Result<Self> {
        // 打开设备文件
        let mem_file = File::options()
            .read(true)
            .write(true)
            .open("/dev/mem")
            .map_err(|e: io::Error| io::Error::new(io::ErrorKind::Other, e))?;

        // 设置映射选项，包括偏移量
        let mmap = unsafe {
            MmapOptions::new()
                .offset(phys_addr)
                .len(size)
                .map_mut(&mem_file)?
        };

        Ok(Self { mmap })
    }

    /// 读取一个 32 位整数（volatile）
    pub fn read_u32(&self, offset: usize) -> u32 {
        unsafe {
            let ptr = self.mmap.as_ptr().offset(offset as isize) as *const u32;
            ptr.read_volatile()
        }
    }

    /// 写入一个 32 位整数（volatile）
    pub fn write_u32(&mut self, offset: usize, value: u32) {
        unsafe {
            let ptr = self.mmap.as_mut_ptr().offset(offset as isize) as *mut u32;
            ptr.write_volatile(value);
        }
    }

    /// 读取一个 64 位整数（volatile）
    pub fn read_u64(&self, offset: usize) -> u64 {
        unsafe {
            let ptr = self.mmap.as_ptr().offset(offset as isize) as *const u64;
            ptr.read_volatile()
        }
    }

    /// 写入一个 64 位整数（volatile）
    pub fn write_u64(&mut self, offset: usize, value: u64) {
        unsafe {
            let ptr = self.mmap.as_mut_ptr().offset(offset as isize) as *mut u64;
            ptr.write_volatile(value);
        }
    }
}

impl Drop for Regi {
    fn drop(&mut self) {
        // 内存映射会在 MmapMut 被 drop 时自动解除
    }
}
